package com.bflow.impl;


import com.bflow.api.*;
import com.bflow.api.bo.*;
import com.bflow.api.bo.ext.BflowUserBo;
import com.bflow.api.constant.BflowConstant;
import com.bflow.api.constant.BflowConstant.*;
import com.bflow.commons.bean.OrderBean;
import com.bflow.commons.bean.PageResult;
import com.bflow.commons.consts.CommConst;
import com.bflow.commons.exception.BizException;
import com.bflow.commons.service.BaseService;
import com.bflow.commons.util.StrUtil;
import com.bflow.dao.BflowRunInstanceDao;
import com.bflow.dao.po.BflowRunInstancePo;
import com.bflow.util.AssertUtil;
import com.bflow.util.BeanMapper;
import com.bflow.util.JsonUtil;
import com.bflow.util.SysSeqUtil;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.Date;
import java.util.List;

/**
 * 功能：BflowRunInstance 流程实例-实现业务接口
 * 作者：haowen
 * 日期：2018-12-13
 */
@Service("bflowRunInstanceService")
public class BflowRunInstanceServiceImpl extends BaseService<BflowRunInstancePo, BflowRunInstanceBo> implements IBflowRunInstanceService<BflowRunInstancePo, BflowRunInstanceBo> {

	@Autowired
	private BflowRunInstanceDao dao;

	@Autowired
	private IBflowDefineProcessService<?, BflowDefineProcessBo> bflowDefineProcessService;
    @Autowired
    private IBflowRunTaskService<?, BflowRunTaskBo> bflowRunTaskService;
    @Autowired
    private IBflowDefineActService<?, BflowDefineActBo> bflowDefineActService;
    @Autowired
    private IBflowRunRecordService<?, BflowRunRecordBo> bflowRunRecordService;
    @Autowired
    private IBflowDefineActRouteService<?, BflowDefineActRouteBo> bflowDefineActRouteService;


	@Override
	protected BflowRunInstanceDao getDao() {
		return dao;
	}

    /**
     * 插入流程实例数据
     * @param bo
     * @return
     */
    @Override
	public String add(BflowRunInstanceBo bo) {
	    if(StringUtils.isBlank(bo.getId()))
            bo.setId(SysSeqUtil.genId());
		bo.setCreateTime(new Date());
        bo.setUpdateTime(new Date());
        validAddInfo(bo);
		BflowRunInstancePo po = BeanMapper.map(bo, BflowRunInstancePo.class);
		if (dao.insert(po) <= 0) {
			throw new BizException("新增失败");
		}
		return bo.getId();
	}

    @Override
    public BflowRunInstanceBo get(String instanceId, String userId, boolean getRecords) throws BizException {
        BflowRunInstanceBo instanceBo = findById(instanceId);
        if(getRecords){
            BflowDefineProcessBo processBo = bflowDefineProcessService.getFlowModelBykey(instanceBo.getProcKey());
            String[] allActArray = processBo.getActArray();
            String[] allProcessUserIds = getProccessUserIds(allActArray);
            List<BflowRunRecordBo> processRecords = bflowRunRecordService.getFullList(instanceBo, allActArray, allProcessUserIds, userId);
            instanceBo.setRecords(processRecords);//获取记录
        }
        return instanceBo;
    }

    //获取 处理人列表
    @Override
    public String[] getProccessUserIds(String[] allActArray){
        String[] userIds = new String[allActArray.length];
        List<BflowDefineActBo> actBoList = bflowDefineActService.findByIds(allActArray);
        for (int i = 0; i < allActArray.length; i++) {
            for (BflowDefineActBo actBo : actBoList) {
                if(actBo.getId().equals(allActArray[i])){
                    String assigneeType = actBo.getAssigneeType();
                    if(ActAssignTypes.ASSIGN_ROLE_KEY.eq(assigneeType)
                            || ActAssignTypes.ASSIGN_USER_ID.eq(assigneeType)){
                        userIds[i] = ActAssignTypes.get(assigneeType).getTitle(); //分派类型 填充中文
                    }else {
                        userIds[i] = actBo.getAssignee();
                    }
                }
            }
        }
        return userIds;
    }

    @Override
    public PageResult<BflowRunInstanceBo> getList(String startUid, String processId) throws BizException {
      AssertUtil.isNotNull(startUid,"用户id不能为空");
      BflowRunInstanceQueryParam queryParam=new BflowRunInstanceQueryParam();
      BflowRunInstanceBo query=new BflowRunInstanceBo();
      query.setStartUid(startUid);
      queryParam.setBflowRunInstance(query);
      queryParam.addExtendParam("BUSI_KEY"," ");
      PageResult<BflowRunInstanceBo> page = this.findPage(queryParam);
        if(CollectionUtils.isNotEmpty(page.getResult())){
          for(BflowRunInstanceBo b:(List<BflowRunInstanceBo>)page.getResult()){
              //请假申请跳过 并删除
              if(b.getProcKey().equals("leaveApply")){
                  ((List<BflowRunInstanceBo>)page.getResult()).remove(b);
                continue;
              }
              String procKey = b.getProcKey();
              BflowDefineProcessBo process = bflowDefineProcessService.getFlowModelBykey(procKey);
              if(process!=null){
                  b.setProcess(process);
                  BflowRunTaskBo taskQuery=new BflowRunTaskBo();
                  taskQuery.setProcId(process.getId());
                  taskQuery.setInstanceId(b.getId());
                  taskQuery.setStatus(BflowConstant.RunTaskStatus.WAITING_DEAL.getCode());
                  BflowRunTaskBo task = bflowRunTaskService.getOneBy(taskQuery);
                  if(task!=null){
                      BflowDefineActBo currAct = bflowDefineActService.findById(task.getActId());
                      b.setCurrAct(currAct);
                  }
                  b.setStatus(FlowRunInstanceStatus.get(b.getStatus()).getTitle());
              }

          };
          page.setTotalCount(page.getResult().size());
        }
      return  page;
    }




    /**
     * 启动流程
     * @param instanceBo
     * @throws BizException
     * 作者：liaofeng
     */
    @Transactional
	@Override
	public void startProcess(BflowRunInstanceBo instanceBo) throws BizException {
        //参数校验（基本信息，用户信息）
        this.validStartProcessInfo(instanceBo);
        //查询流程是否定义
        BflowDefineProcessBo defineProcessBo = getDefineProcess(instanceBo.getProcKey());
        AssertUtil.isNotNull(defineProcessBo, "流程KEY "+instanceBo.getProcKey()+" 未定义");
        String procInsId = instanceBo.getId();
        String procKey = instanceBo.getProcKey();
        String busiId = instanceBo.getBusiId();
        logger.info("============提交的流程参数:" + JsonUtil.toStr(instanceBo));
		// 启动流程
		try {
            instanceBo.setStatus(FlowRunInstanceStatus.RUN.getCode());
            instanceBo.setVersion(bflowDefineProcessService.getLastProcessVersion(defineProcessBo.getKey()));
            //保存流程实例信息
            procInsId = add(instanceBo);

            //开启流程后进入流程的第一个任务节点
            intoFirstActTaskNode(instanceBo, defineProcessBo);
		} catch (Exception e) {
			logger.error("流程启动失败！", e);
            throw new BizException("流程启动失败！", e);
		}
	}

    /**
     * 是否有定义流程
     * @param procKey
     * @return
     */
	private BflowDefineProcessBo getDefineProcess(String procKey){
		if(StringUtils.isBlank(procKey))
			return null;
        BflowDefineProcessBo defineProcessBo = null;
        if(procKey.startsWith(BflowConstant.PROCESS_KEY_TPL_PREFIX)){
            String id = procKey.split(BflowConstant.PROCESS_KEY_TPL_PREFIX)[1];
            defineProcessBo = bflowDefineProcessService.findById(id);
        }else{
            defineProcessBo = bflowDefineProcessService.getFlowModelBykey(procKey);
        }
		return defineProcessBo;
	}

    /**
     * 验证基础信息
     * @param bflowRunInstanceBo
     * 作者：liaofeng
     */
    public void validBasicsInfo(BflowRunInstanceBo bflowRunInstanceBo) {
        AssertUtil.isNotNull(bflowRunInstanceBo, "提交的流程参数 不能为空");
        AssertUtil.isNotBlank(bflowRunInstanceBo.getProcKey(), "流程定义Key 不能为空");
        AssertUtil.isNotBlank(bflowRunInstanceBo.getBusiKey(), "业务KEY 不能为空");
        AssertUtil.isNotBlank(bflowRunInstanceBo.getBusiId(), "业务ID 不能为空");
        AssertUtil.isNotBlank(bflowRunInstanceBo.getStartUid(), "发起人ID 不能为空");
        AssertUtil.isNotBlank(bflowRunInstanceBo.getStartUname(), "发起人姓名 不能为空");
    }

    /**
     * 验证开始流程信息
     * @param runInstanceBo
     * 作者：liaofeng
     */
	public void validStartProcessInfo(BflowRunInstanceBo runInstanceBo) {
       this.validBasicsInfo(runInstanceBo);
	}

    /**
     * 验证新增流程实例信息
     * @param runInstanceBo
     * 作者：liaofeng
     */
    public void validAddInfo(BflowRunInstanceBo runInstanceBo) {
        this.validBasicsInfo(runInstanceBo);
        AssertUtil.isNotBlank(runInstanceBo.getId(), "流程实例ID 不能为空");
        AssertUtil.isNotNull(runInstanceBo.getCreateTime(), "创建时间 不能为空");
    }

    /**
     * 进入 第一个节点
     * @param runInstanceBo
     * 作者：liaofeng
     */
    private void intoFirstActTaskNode(BflowRunInstanceBo runInstanceBo, BflowDefineProcessBo defineProcessBo){
        if(defineProcessBo != null){
            //获取开始活动节点
            String startActId = defineProcessBo.getStartActId();
            if(StringUtils.isNotBlank(startActId)){
                //先查询节点信息
                BflowDefineActBo defineActBo = bflowDefineActService.findById(startActId);
                if(defineActBo != null){
                    //获取下个处理人
                    String assignee = bflowRunTaskService.getNextActAssignee(0, null, defineActBo, defineProcessBo);
                    //兼容默认审批
                    if(DefActKey.DEF_USER_AUDIT.eq(defineActBo.getKey()))
                        startActId = startActId+BflowConstant.UNDERLINE+"0";
                    bflowRunTaskService.addNextTask(startActId, assignee, runInstanceBo.getId(), defineProcessBo.getId(), new BflowRunTaskAuditBo(runInstanceBo.getStartUid(), runInstanceBo.getStartUname()));
                }
            }
        }
    }


    /**
     * 停止流程
     * @param instanceId 流程实例ID
     * @throws BizException
     * 作者：liaofeng
     */
    @Transactional
	@Override
	public void stopProcess(String instanceId) throws BizException {
        AssertUtil.isTrue(StringUtils.isNotBlank(instanceId), "流程实例ID 不能为空");

        updateFinishStatus(instanceId);
	}

    /**
     * 停止流程
     * @param busiKey 业务KEY
     * @param busiId 业务ID
     * @param busiSubId 子业务ID
     * @throws BizException
     * 作者：liaofeng
     */
    @Transactional
	@Override
	public void stopProcess(String busiKey, String busiId, String busiSubId) throws BizException {
        AssertUtil.isNotNull(busiKey, "业务KEY 不能为空");
        AssertUtil.isNotNull(busiId, "业务ID 不能为空");
        AssertUtil.isNotNull(busiSubId, "子业务ID 不能为空");

        //先查询这个流程
        BflowRunInstanceBo queryBo = new BflowRunInstanceBo();
        queryBo.setBusiKey(busiKey);
        queryBo.setBusiId(busiId);
        queryBo.setBusiSubId(busiSubId);
        BflowRunInstanceBo runInstanceBo = this.getOneBy(queryBo);
        AssertUtil.isNotNull(runInstanceBo, "流程实例不存在");
        AssertUtil.isTrue(StringUtils.isNotBlank(runInstanceBo.getId()), "流程实例ID 不能为空");

        //再停止更新流程
        updateFinishStatus(runInstanceBo.getId());
	}

    /**
     * 更新完成状态
     * @param instanceId
     * 作者：liaofeng
     */
	private void updateFinishStatus(String instanceId){
        BflowRunInstanceBo updateBo = new BflowRunInstanceBo();
        updateBo.setId(instanceId);
        updateBo.setStatus(FlowRunInstanceStatus.FINISH.getCode());
        updateBo.setEndTime(new Date());
        updateBo.setUpdateTime(new Date());

        int update = this.update(updateBo);
        if (update <= 0) {
            throw new BizException("停止流程失败");
        }
    }

    /**
     * 更新业务数据
     * @param runInstanceBo
     * 作者：liaofeng
     */
    @Transactional
	@Override
	public void updateBizData(BflowRunInstanceBo runInstanceBo) throws BizException {
        AssertUtil.isNotNull(runInstanceBo, "更新的流程实例内容 不能为空");
        AssertUtil.isTrue(StringUtils.isNotBlank(runInstanceBo.getId()), "更新的流程实例ID 不能为空");

        int update = this.update(runInstanceBo);
        if (update <= 0) {
            throw new BizException("更新业务数据失败");
        }
	}

    /**
     * 查询业务数据
     * @param busiKey 业务KEY
     * @param busiId 业务ID
     * @param busiSubId 子业务ID
     * @return
     * @throws BizException
     * 作者：liaofeng
     */
	@Override
	public BflowRunInstanceBo findByBsId(String busiKey, String busiId, String busiSubId) throws BizException{
        AssertUtil.isNotNull(busiKey, "业务KEY 不能为空");
        AssertUtil.isNotNull(busiId, "业务ID 不能为空");
        AssertUtil.isNotNull(busiSubId, "子业务ID 不能为空");

        //先查询这个流程
        BflowRunInstanceBo queryBo = new BflowRunInstanceBo();
        queryBo.setBusiKey(busiKey);
        queryBo.setBusiId(busiId);
        queryBo.setBusiSubId(busiSubId);
        BflowRunInstanceBo runInstanceBo = this.getOneBy(queryBo);
        if(runInstanceBo != null)
            return runInstanceBo;
		return null;
	}

    @Override
    public PageResult<BflowRunInstanceBo> findPage(BflowRunInstanceQueryParam queryParam, String busiKey) {
        BflowRunInstanceBo queryBo = queryParam.getBoIfNullAndNew();
        queryBo.setBusiKey(busiKey);
        queryParam.setBflowRunInstance(queryBo);
        queryParam.setDefParam(1, 10, "CREATE_TIME", "DESC");
        return findPage(queryParam);
    }

    /**
     * 流程退回一步 流程至少要在第二步骤才允许操作
     * @param instancesId
     * @throws BizException
     */
    @Transactional
    @Override
    public void backByInstanceId(String instancesId, BflowUserBo user) throws BizException {
        AssertUtil.isNotNull(instancesId,"流程申请实例id不能为空");
        AssertUtil.isNotNull(user,"用户信息不能为空");
        BflowRunInstanceBo instance = this.findById(instancesId);
        AssertUtil.isNotNull(instance,"流程申请实例id无效");
        BflowDefineProcessBo flowMode = bflowDefineProcessService.getFlowModelBykey(instance.getProcKey());
        BflowRunRecordQueryParam query=new BflowRunRecordQueryParam();
        BflowRunRecordBo recordQuery=new BflowRunRecordBo();
        recordQuery.setInstanceId(instancesId);
        query.setOrderBean(new OrderBean("CREATE_TIME","desc"));
        query.setBflowRunRecord(recordQuery);
        List<BflowRunRecordBo> byParam = bflowRunRecordService.findByParam(query);
        if(CollectionUtils.isEmpty(byParam)){
           throw new BizException("申请流程还在第一步骤不允许回退");
        }
        //更新操作历史最新一条记录至为失效
        BflowRunRecordBo bflowRunRecordBo = byParam.get(0);
        bflowRunRecordBo.setDelFlag(CommConst.STATUS_N);
        bflowRunRecordBo.setCreateUid(user.getUserId());
        bflowRunRecordBo.setCreateUname(user.getUserName());
        bflowRunRecordService.update(bflowRunRecordBo);

        //更新目前最新处理人记录未失效
        BflowRunTaskBo taskQuery=new BflowRunTaskBo();
        taskQuery.setProcId(flowMode.getId());
        taskQuery.setInstanceId(instancesId);
        taskQuery.setStatus(RunTaskStatus.WAITING_DEAL.getCode());
        BflowRunTaskBo currTask = bflowRunTaskService.getOneBy(taskQuery);
        currTask.setDelFlag(CommConst.STATUS_N);
        bflowRunTaskService.update(currTask);
        //在写入上一次处理任务
        String backTaskId = bflowRunRecordBo.getTaskId();
        BflowRunTaskBo backTask = bflowRunTaskService.findById(backTaskId);
        backTask.setDelFlag(CommConst.STATUS_N);
        bflowRunTaskService.update(backTask);
        backTask.setDelFlag(CommConst.STATUS_Y);
        backTask.setStatus(RunTaskStatus.WAITING_DEAL.getCode());
        bflowRunTaskService.add(backTask);
    }
}
